<template>
  <div class="file-tree">
    <!--鼠标右键菜单栏 -->
    <div v-show="showRightMenu">
      <ul id="menu" class="right-menu">
        <li class="menu-item" @click="openCreateFile">新建文件</li>
        <li class="menu-item" @click="openCreateDirectory">新建目录</li>
        <li class="menu-item" @click="deleteFile">删除</li>
        <li class="menu-item" @click="renameVisible = true">重命名</li>
      </ul>
    </div>
    <el-tree :data="fileTreeData" :props="defineProps" @node-click="handleNodeClick" @node-contextmenu="handleContextmenu"/>

    <!-- 新建文件操作框 -->
    <el-dialog width="30%" v-model="createFileVisible" title="新建文件" center>
      <el-input
        v-model="newFileName"
        @keydown.esc="createFileVisible = false" 
        @keydown.enter="addFile"
        placeholder="回车/ESC(确认/取消)" />
    </el-dialog>

    <!-- 新建目录操作框 -->
    <el-dialog width="30%" v-model="createDirectoryVisible" title="新建目录" center>
      <el-input
        v-model="newDirectoryName"
        @keydown.esc="createDirectoryVisible = false" 
        @keydown.enter="addDirectory"
        placeholder="回车/ESC(确认/取消)" />
    </el-dialog>

    <!-- 重命名 -->
    <el-dialog width="30%" v-model="renameVisible" title="重命名" center>
      <div class="center">
        <el-input v-model="renameFileName"/>
      </div>
      <div class="button-group">
        <div>
          <button @click="renameVisible = false" class="cancel">取消</button>
        </div>
        <div>
          <button @click="renameFile" class="confirm">确定</button>
        </div>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { computed, defineComponent, ref, watch } from 'vue'
import { getFile, createFile, createDirectory, deleteAll, rename } from "@/api/index"

import { ElMessage, ElMessageBox } from 'element-plus'

import bus from '../../../config/bus'
import { useRouter } from 'vue-router'

export default defineComponent({
  name: "file-tree",

  props: {
    name: {
      type: String,
      required: true
    },
    treeData: {
      type: Array,
      required: true
    }
  },

  setup(props, context) {
    const router = useRouter()

    //重命名的新文件名
    const renameFileName = ref('')

    //创建文件时输入的新文件名（含后缀）
    const newFileName = ref('')

    //创建文件夹时的文件夹名称
    const newDirectoryName = ref('')

    //是否显示“新建文件操作对话框”
    const createFileVisible = ref(false)

    //是否显示“新建目录操作对话框”
    const createDirectoryVisible = ref(false)

    //是否显示“重命名操作对话框”
    const renameVisible = ref(false)

    //是否显示鼠标右键菜单
    const showRightMenu = ref(false)
    //鼠标右键时操作的目录或者文件数据
    let rightMenuData = null
    let rightMenuNode = null

    const preNodeId = ref(null)
    const curNodeId = ref(null)
    const nodeCount = ref(0)
    const nodeTimer = () => {console.log(1)}

    //得到父组件传过来的项目名和展示的目录树信息，通过计算属性保证了响应性
    const projectName = computed(() => props.name)
    const fileTreeData = computed(() => props.treeData)

    const defineProps = {
      children: 'children',
      label: 'name',
    }

    //监视renameVisible，当为true时，将选中的值填入
    watch(renameVisible, newVal => {
      //只有当新值为true时，也就是显示重命名对话框时，才对绑定的value进行赋值
      if (newVal) {
        renameFileName.value = rightMenuData.name
      } else {
        renameFileName.value = ''
      }
    })

    //鼠标点击时触发，修改为鼠标双击时触发
    const handleNodeClick = (data, node, prop) => {
      nodeCount.value++
      if(preNodeId.value && nodeCount.value >= 2) {
        curNodeId.value = node.id 
        nodeCount.value = 0
        //第一次点击的节点和第二次点击的节点id相同
        if(curNodeId.value == preNodeId.value) {
          if (data.name.lastIndexOf('.') != -1) {
            //发起请求获取双击文件的内容
            getFile(data.path).then(res => {
              if (res.code == 0) {
                //将得到的内容发送给兄弟组件code-editor
                bus.emit('showCode', {
                  code: res.data,
                  info: {
                    projectName: projectName.value,
                    data
                  }
                })
              } else {
                ElMessage.error(res.msg)
              }
            })
          }
          curNodeId.value = null
          preNodeId.value = null
          return
        }
      }
      preNodeId.value = node.id
      nodeTimer.value = setTimeout(() => { //300ms内没有第二次点击就把第一次点击的清空
        preNodeId.value = null
        nodeCount.value = 0
      }, 300)   
    }

    //关闭鼠标右键时展开的菜单
    const closeRightMenu = () => {
      showRightMenu.value = false
      // 及时关掉鼠标监听事件
      document.removeEventListener('click', closeRightMenu)
    }

    //关闭创建文件的菜单
    const closeCreateFile = () => {
      console.log('close')
      createFileVisible.value = false
      document.removeEventListener('click', closeCreateFile)
    }
    
    //鼠标右击时触发
    const handleContextmenu = (event, data, node, obj) => { 
      showRightMenu.value = true
      let menu = document.querySelector('#menu')
      menu.style.left = event.clientX + 'px'
      menu.style.top = event.clientY + 'px'
      // 给整个document添加监听鼠标事件，点击任何位置执行closeRightMenu方法，及时将菜单关闭
      document.addEventListener('click', closeRightMenu)
      // data.children.push({path: 'xxx', name: 'x', children: []})
      rightMenuData = data
      rightMenuNode = node
    }

    //点击创建文件菜单栏时触发的事件
    const openCreateFile = () => createFileVisible.value = true

    //点击创建目录菜单栏时触发的事件
    const openCreateDirectory = () => createDirectoryVisible.value = true

    //新建文件
    const addFile = () => {
      if (newFileName.value.length == 0) {
        ElMessage.error('文件名不能为空')
        return
      }
      createFileVisible.value = false
      createFile(rightMenuData.path + "/" + newFileName.value, newFileName.value)
        .then((res) => {
          if (res.code == 0) {
            //创建成功
            rightMenuData.children.push({path: rightMenuData.path + "/" + newFileName.value, name: newFileName.value, children: []})
            //是文件，默认打开
            bus.emit('addAndOpen', {
              name: newFileName.value,
              path: rightMenuData.path + "/" + newFileName.value,
              code: ''
            })
            newFileName.value = ''
          } else {
            ElMessage.error(res.msg)
          }
        })
    }

    //新建目录
    const addDirectory = () => {
      if (newDirectoryName.value.length == 0) {
        ElMessage.error('目录不能为空')
        return
      }
      createDirectoryVisible.value = false
      createDirectory(rightMenuData.path + "/" + newDirectoryName.value, newDirectoryName.value)
        .then((res) => {
          if (res.code == 0) {
            //创建成功
            rightMenuData.children.push({path: rightMenuData.path + "/" + newDirectoryName.value, name: newDirectoryName.value, children: []})
            newDirectoryName.value = ''
          } else {
            ElMessage.error(res.msg)
          }
        })
    }

    //删除
    const deleteFile = () => {
      ElMessageBox.confirm('你正在执行删除操作，是否继续？', '提示', {
          confirmButtonText: '删除',
          cancelButtonText: '取消',
          type: 'warning',
        }
      ).then(() => {
        //调用api在后台删除，找到当前节点的父节点，由父节点删除当前节点
        let isRoot = rightMenuNode.parent.parent == null
        deleteAll(rightMenuData.path, isRoot).then(res => {
          if (res.code == 0) {
            //如果删除的文件在tabs中，从tabs中删除
            bus.emit('deleteTabs', { path: rightMenuData.path })
            //删除成功，前端展示节点进行删除
            rightMenuNode.remove()
          } else {
            ElMessage.warning(res.msg)
          }
        })
        ElMessage({
          type: 'success',
          message: '删除成功',
        })
        //如果是根节点被删除，就回退到dashboard
        if (isRoot) {
          router.back(-1)
        }
      })
    }

    //对文件或者目录重命名
    const renameFile = () => {
      //新文件名不能和原文件名相同
      if (rightMenuData.name === renameFileName.value) {
        ElMessage.error('新文件名不能和原文件名相同')
        return
      }
      if (renameFileName.value.length === 0) {
        ElMessage.error("文件名不能为空")
        return
      }
      //发起请求修改文件名
      const newName = renameFileName.value
      const newPath = rightMenuData.path.substring(0, rightMenuData.path.lastIndexOf(rightMenuData.name)) + newName
      //从当前节点开始递归向下，相关影响到的节点都需要改名
      rename(rightMenuData.path, newPath, rightMenuNode.parent.parent == null).then(res => {
        if (res.code === 0) {
          bus.emit('clearTabs', {})
          //递归更改子节点的数据
          renamePath(rightMenuData, rightMenuData.name, newName)
          rightMenuData.name = newName
        } else {
          ElMessage.warning(res.msg)
        }
      })
      renameVisible.value = false
    }

    //递归修改路径
    const renamePath = (root, oldStr, newStr) => {
      if (root == null) return
      console.log(root)
      //将当前root的oldStr全部替换为newStr
      let temp = root.path.split('/').map(val => val === oldStr ? newStr : val)
      let newPath = ''
      for (let i = 0; i < temp.length; i++) {
        newPath += temp[i]
        if (i != temp.length - 1) newPath += '/'
      }
      root.path = newPath
      if (root.children == null) return
      root.children.forEach(item => renamePath(item, oldStr, newStr))
    }

    return {
      handleNodeClick,
      fileTreeData,
      defineProps,
      projectName,
      handleContextmenu,
      showRightMenu,
      addFile,
      deleteFile,
      renameFile,
      createFileVisible,
      openCreateFile,
      newFileName,
      renameVisible,
      renameFileName,
      openCreateDirectory,
      createDirectoryVisible,
      newDirectoryName,
      addDirectory
    }
  },
})
</script>

<style lang="scss">
.file-tree {
  overflow: auto;
  padding: 10px;

  .el-dialog {
    border-radius: 15px;

    .el-dialog__header {
      border-bottom: 1px solid #DCDCDC;
      margin: 0;

      .el-dialog__title {
        font-size: 14px;
      }
    }

    .el-dialog__body {
      padding: 20px;

      .button-group {
        display: flex;
        justify-content: flex-end;
        
        button {
          cursor: pointer;
          color: white;
          height: 32px;
          margin-top: 10px;
          border: 0px;
          border-radius: 5px;
        }

        .cancel {
          width: 70px;
          background-color: #F1F1F2;
          border: 1px solid #DCDCDC;
          color: #474F5F;
          margin-right: 10px;
        }

        .cancel:hover {
          background-color: #DBDBDC;
          border: 1px solid #DCDCDC;
        }

        .confirm {
          width: 60px;
          background-color: #5590FF;
          margin-right: 20px;
        }

        .confirm:hover {
          background-color: #3C7FFF;
        }
      }
    }
  }

  .right-menu {
    z-index: 999;
    position: absolute;
    height: 130px;
    width: 120px;
    position: absolute;
    border-radius: 5px;
    border: 1px solid #ccc;
    background-color: #F1F1F1;
    cursor: pointer;

    .menu-item {
      line-height: 20px;
      text-align: left;
      margin-top: 10px;
      font-size: 12px;
      color: #4E6572;
      list-style: none;
      padding-left: 20px;
      border-radius: 5px;
    }
    
    li:hover {
      background-color: #E0E0E0;
      color: #1E283B;
    }
  }

  .el-tree {
    background-color: #F5F5F5;
  }
}
</style>